package com.gitee.wsl.struct.generator.sample

import com.gitee.wsl.ext.base.MAX_RADIX
import com.gitee.wsl.ext.base.currentTimeMillis
import com.gitee.wsl.ext.string.toBinaryString
import com.gitee.wsl.struct.generator.sample.CardIdGeneratorable.Companion.getTimeStampSecond
import kotlinx.atomicfu.locks.SynchronizedObject
import kotlinx.atomicfu.locks.synchronized
import kotlin.jvm.JvmOverloads
import kotlin.random.Random


/**
 * 16位订单ID生成器
 * 订单ID固定为16位，53bit，格式如下（各字段位数可通过全参构造函数调整）：
 * +======================================================================
 * | 30bit时间戳 | 3bit机器编号  | 4bit订单类型  | 10bit序号 | 6bit 校验位 |
 * +======================================================================
 * 1. 30 bit的秒时间戳支持34年
 * 2. 3 bit机器编号支持8台负载
 * 3. 4 bit订单类型支持16种订单类型
 * 4. 10 bit序号支持1024个序号
 * 5. 6 bit 校验位目前对店铺编号+原始ID进行校验
 * 即订单生成最大支持8台负载，每台负载每秒钟可以生成512个订单ID。
 * 时间戳、机器编号、序号和校验位的bit位数支持业务自定义，方便业务定制自己的生成器。
 *
 * @author Mingo.Liu
 * @date 2023-11-28
 */
class OrderIdGenerator: SynchronizedObject {
    /**
     * 时间bit数，时间的单位为秒，30 bit位时间可以表示34年
     */
    private var timeBits = 30

    /**
     * 机器编码bit数
     */
    private var machineBits = 3

    /**
     * 类型bit数
     */
    private var typeBites = 4

    /**
     * 每秒序列bit数
     */
    private var sequenceBits = 10

    /**
     * 校验bit位数
     */
    private var validationBits = 6

    /**
     * 上一次时间戳
     */
    private var lastStamp = -1L

    /**
     * 序列
     */
    private var sequence = 0L

    /**
     * 机器编号
     */
    private var machineId = 1L

    /**
     * 时间左移bit数
     */
    private var timeOffset = 0

    /**
     * 机器编码左移bit数
     */
    private var machineOffset = 0

    /**
     * 类型左移bit数
     */
    private var typeOffset = 0

    /**
     * 序列左移bit数
     */
    private var sequenceOffset = 0

    /**
     * 最大序列号
     */
    private var maxSequence = 0L

    /**
     * 最大校验码
     */
    private var maxCode = 0

    /**
     * 最大订单类型
     */
    private var maxType = 0

    /**
     * 起始时间戳
     */
    private var startTimeStamp = 0L

    /**
     * 开始时间格式，默认"2023-01-01 00:00:00"
     */
    private var startTimeString = "2020-01-01 00:00:00"

    @JvmOverloads
    constructor(machineId: Int = 1) {
        val maxMachineId = (-1 shl machineBits).inv()
        if (machineId > maxMachineId) {
            throw IllegalArgumentException("Machine bits is $machineBits, so the max machine id is $maxMachineId")
        }

        this.machineId = machineId.toLong()
        init()
    }

    /**
     * 全参构造函数，便于业务定制订单号生成器
     *
     * @param timeBits        时间bit数
     * @param machineBits     机器编码bit数
     * @param typeBits        订单类型bit数
     * @param sequenceBits    每秒序列bit数
     * @param validationBits  校验bit位数
     * @param machineId       机器编号
     * @param startTimeString 开始时间
     */
    constructor(
        timeBits: Int,
        machineBits: Int,
        typeBits: Int,
        sequenceBits: Int,
        validationBits: Int,
        machineId: Int,
        startTimeString: String?,
    ) {
        if (timeBits <= 0 || machineBits <= 0 || typeBits <= 0 || sequenceBits <= 0 || validationBits <= 0) {
            throw IllegalArgumentException("The bits should be larger than 0")
        }
        if (timeBits + machineBits + typeBits + sequenceBits + validationBits != ID_BIT_LENGTH) {
            throw IllegalArgumentException("The sum of timeBits and machineBits and typeBits and sequenceBits and validationBits should be 53")
        }

        val maxMachineId = (-1 shl machineBits).inv()
        if (machineId > maxMachineId) {
            throw IllegalArgumentException("Machine bits is $machineBits, so the max machine id is $maxMachineId")
        }

        this.timeBits = timeBits
        this.machineBits = machineBits
        this.typeBites = typeBits
        this.sequenceBits = sequenceBits
        this.validationBits = validationBits
        this.machineId = machineId.toLong()
        if (null != startTimeString) {
            this.startTimeString = startTimeString
        }
        init()
    }

    /**
     * 根据给定的系统编号生成订单ID
     *
     * @param shopId 店铺编号
     * @return 16位订单ID
     */

    fun generate(shopId: String, type: Int): Long = synchronized(this){
        if (shopId.isEmpty()) {
            throw IllegalArgumentException("Shop id cannot be null")
        }
        if (type < 0 || type > maxType) {
            throw IllegalArgumentException("Type is invalid")
        }

        var curStamp = currentSecond
        if (curStamp < lastStamp) {
            throw IllegalArgumentException("Clock moved backwards. Refusing to generate id")
        }

        if (curStamp == lastStamp) {
            sequence = (sequence + 1) and maxSequence
            if (sequence == 0L) {
                curStamp = nextSecond
                sequence = Random.nextLong(20)
            }
        } else {
            sequence = Random.nextLong(20)
        }
        lastStamp = curStamp

        return combine(shopId, type.toLong(), curStamp - startTimeStamp)
    }

    /**
     * 校验订单号是否合法
     *
     * @param shopId 店铺编号
     * @param id     订单号
     * @return boolean 合法返回true，反之false
     */
    fun validate(shopId: String, id: Long): Boolean {
        if (id > MAX_ID || id < MIN_ID || shopId.isEmpty()) {
            return false
        }

        val bitString: String = id.toString().toBinaryString()
        val bitLength = bitString.length
        val codeBitString = bitString.substring(bitLength - validationBits)
        val parsedValidationCode = codeBitString.toInt(2)
        val originId = id - parsedValidationCode
        val validationCode = getValidationCode(originId, shopId, maxCode)
        if (parsedValidationCode != validationCode) {
            return false
        }

        val parsedTimestamp = bitString.substring(0, bitLength - timeOffset).toLong(2)
        val currentStamp: Long = currentTimeMillis / 1000 - startTimeStamp
        val timeDelta = parsedTimestamp - currentStamp
        return timeDelta < 10
    }

    /**
     * 解析订单ID
     *
     * @param id 订单ID
     * @return 解析结果依次是时间戳(毫秒)、机器编码、订单类型、序列号
     */
    fun parse(shopId: String, id: Long): Array<Long> {
        if (!validate(shopId, id)) {
            throw IllegalArgumentException("Id is invalid")
        }

        val bitString: String = id.toString().toBinaryString()
        val bitLength = bitString.length
        val timestamp = bitString.substring(0, bitLength - timeOffset).toLong(2)
        val machineId =
            bitString.substring(bitLength - machineOffset - machineBits, bitLength - machineOffset)
                .toLong(2)
        val type = bitString.substring(bitLength - typeOffset - typeBites, bitLength - typeOffset)
            .toLong(2)
        val sequence = bitString.substring(
            bitLength - sequenceOffset - sequenceBits,
            bitLength - sequenceOffset
        ).toLong(2)
        return arrayOf((timestamp + startTimeStamp) * 1000, machineId, type, sequence)
    }

    /**
     * 将时间戳、机器编号、序号组合成订单ID
     *
     * @param shopId    店铺ID
     * @param type      订单类型
     * @param timestamp 时间戳
     * @return 订单ID
     */
    protected fun combine(shopId: String, type: Long, timestamp: Long): Long {
        val originId = timestamp shl timeOffset or (machineId shl machineOffset
                ) or (type shl typeOffset
                ) or (sequence shl sequenceOffset)

        val validationCode = getValidationCode(originId, shopId, maxCode)
        return originId + validationCode
    }

    /**
     * 数据初始化
     */
    private fun init() {
        sequenceOffset = validationBits
        typeOffset = sequenceOffset + sequenceBits
        machineOffset = typeOffset + typeBites
        timeOffset = machineOffset + machineBits
        maxSequence = (-1L shl sequenceBits).inv()
        maxType = (-1 shl typeBites).inv()
        maxCode = (-1 shl validationBits).inv()
        startTimeStamp = getTimeStampSecond(startTimeString)
    }

    private val currentSecond: Long
        /**
         * 获取当前时间戳 单位秒
         *
         * @return 时间戳（秒）
         */
        get() = currentTimeMillis / 1000

    private val nextSecond: Long
        /**
         * 获取下一秒钟
         *
         * @return 时间戳（秒）
         */
        get() {
            var second = currentSecond
            while (second <= lastStamp) {
                //IdUtils.sleep(20)
                second = currentSecond
            }

            return second
        }

    /**
     * 获取校验码
     *
     * @param originId 原始数字
     * @param shopId   店铺ID
     * @param maxCode  最大校验码
     * @return 校验码
     */
    private fun getValidationCode(originId: Long, shopId: String, maxCode: Int): Int {
        val numberShopId =
            shopId.uppercase().toLong(Char.MAX_RADIX)
        val strId = originId.toString() + numberShopId
        val numbers = IntArray(strId.length)
        run {
            var i = 0
            val length = strId.length
            while (i < length) {
                numbers[i] = strId[i].digitToInt()
                i++
            }
        }
        var i = numbers.size - 2
        while (i >= 0) {
            numbers[i] = numbers[i] shl 1
            numbers[i] = numbers[i] / 10 + numbers[i] % 10
            i -= 2
        }

        var validationCode = 0
        for (number in numbers) {
            validationCode += number
        }
        validationCode *= 9
        return validationCode % maxCode
    }

    companion object {
        /**
         * 最大ID
         */
        private const val MAX_ID = 9999999999999999L

        /**
         * 最小ID
         */
        private const val MIN_ID = 1000000000000000L

        /**
         * ID总bit数
         */
        private const val ID_BIT_LENGTH = 53


    }
}
